<?php
class Template implements ArrayAccess
{
    /**
     * Đường dẫn đến file markup
     * @var string $templateFile
     */
    protected  $templateFile;

    /**
     * Mảng chứa các giá trị key=>value ứng với key đánh dấu và giá trị thực
     * @var array $variable
     */
    protected  $variables = array();

    /**
     * Danh sách các files CSS
     * @var array $cssFiles
     */
    protected  $cssFiles = array();

    /**
     * Danh sách các files Javscript
     * @var array $jsFiles
     */
    protected  $jsFiles = array();

    /**
     * Khởi tạo và gán file template cho lớp
     * @param $templateFile string
     */
    public function __construct($templateFile)
    {
        $this->templateFile = $templateFile;
    }

    /**
     * Kiểm tra tồn tại phần tử
     * @param $offset
     * @return bool
     */
    public function offsetExists($offset)
    {
        return isset($this->variables[$offset]);
    }

    /**
     * Nhận giá trị
     * @param $offset
     * @return mixed
     */
    public function offsetGet($offset)
    {
        if (!$this->offsetExists($offset)) {
            /* BUG: overload ...
            $this->offsetSet($offset, null);
            */
            $this->variables[$offset] = '';
        }
        return $this->variables[$offset];
    }

    /**
     * Đặt giá trị vào
     * @param $offset string
     * @param $value string|array
     */
    public function offsetSet($offset, $value)
    {
        $this->variables[$offset] = $value;
    }

    /**
     * Gỡ bỏ 1 phần tử
     * @param $offset string
     */
    public function offsetUnset($offset)
    {
        unset($this->variables[$offset]);
    }

    /**
     * Thêm một file CSS vào template
     * @param $cssFile string Đường dẫn của file CSS
     */
    public function addCss($cssFile)
    {
        if (!in_array($cssFile, $this->cssFiles)) {
            $this->cssFiles[] = $cssFile;
        }
    }
    /**
     * Thêm một file Javascript
     * @param $jsFile string Đường dẫn của file Javascript cần thêm
     */
    public function addJs($jsFile)
    {
        if (!in_array($jsFile, $this->jsFiles)) {
            $this->jsFiles[] = $jsFile;
        }
    }

    /**
     * Gỡ bỏ một file CSS
     * @param $cssFile string Đường dẫn file CSS
     */
    public function removeCssFile($cssFile)
    {
        if (in_array($cssFile, $this->cssFiles)) {
            $this->cssFiles = array_diff($this->cssFiles, array($cssFile));
        }
    }

    /**
     * Gỡ bỏ một file Javascript
     * @param $jsFiles string Đường dẫn file Javascript
     */
    public  function removeJscriptFile($jsFiles)
    {
        if (in_array($jsFiles, $this->jsFiles)) {
            $this->jsFiles = array_diff($this->jsFiles, array($jsFiles));
        }
    }

    /**
     * Đặt giá trị vào từ 1 mảng dạng key=>value
     * @param $array
     */
    public function setArray($array)
    {
        $this->variables = $array;
    }

    /**
     * Kết xuất Template thành string
     * @return mixed|string
     * @throws Exception
     */
    public function render()
    {
        if (!file_exists($this->templateFile)) {
            throw new Exception("[" . __CLASS__ . "] Không tìm thấy file '{$this->templateFile}'!");
        } else {
            // Nội dung sau khi render
            $content = file_get_contents($this->templateFile);

            /** Thực hiện sinh các tag links & scripts
            dựa trên các file CSS và Javascript đã thêm vào */
            $cssJsCollection = '';
            foreach ($this->cssFiles as $file) {
                $cssJsCollection .= "<link rel=\"stylesheet\" type=\"text/css\" href=\"{$file}\" />";
            }
            foreach ($this->jsFiles as $file) {
                $cssJsCollection .= "<script type=\"text/javascript\" src=\"{$file}\"></script>";
            }

            // Mẫu biểu thức chính quy so khớp với <head>...</head>
            $headPattern = '/<head>([\s\S]*)<\/head>/im';

            // Thực hiện chèn các file CSS & Javascript vào phần head
            $content = preg_replace($headPattern, "<head>$1{$cssJsCollection}</head>", $content);

            // Thay thế những phần tử đánh dấu bằng các giá trị cụ thể
            foreach ($this->variables as $key => $value) {
                /** @var $keyPattern Mẫu tìm key để thay thế
                 *  Dạng {$title} hoặc {$title:NVCTemplate Module#}
                 */
                $keyPattern = '/{\$' . $key .'(:.*?#)?}/';
                if ($value instanceof Template) {
                    $value = $value->render();
                }
                $content = preg_replace($keyPattern , $value, $content);
            }

            /** Thay thế các đánh dấu bằng các giá trị mặc định
             *  Sau khi đã thay thế bằng giá trị (trường hợp người dùng không set)
             */
            $keyPattern = '/\{\$(.+?):(.*?)#\}/im';
            $content = preg_replace($keyPattern, '$2', $content);

            return $content;
        }
    }
}
